﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Common;
using PwdMgr.CommonContract;
using PwdMgr.Utility;
using PwdMgr.DataAccess.Exps;

namespace PwdMgr.DataAccess
{
    public abstract class DBControl : IDBControl
    {
        protected DBControl(string connStr, bool autoCommit = true)
        {
            ThrowUtil.ThrowIfEmptyOrWhiteSpace(connStr);
            Conn = GetConnection(connStr);
            _autoCommit = autoCommit;
        }
        private DbConnection _conn;
        private DbTransaction _transaction;
        private bool _autoCommit;

        public DbTransaction Transaction
        {
            get { return _transaction; }
            set { _transaction = value; }
        }

        public DbConnection Conn
        {
            get { return _conn; }
            protected set { _conn = value; }
        }

        #region abstract methods
        protected abstract DbConnection GetConnection(string connStr);
        #endregion

        public void Open()
        {
            if (Conn.State != System.Data.ConnectionState.Open)
            {
                Conn.Open();
                if (!_autoCommit)
                    Transaction = Conn.BeginTransaction();
            }
        }

        public void Close()
        {
            if (Conn.State != System.Data.ConnectionState.Closed)
                Conn.Close();
        }

        public void Commit()
        {
            if (_autoCommit)
                return;
            if (Transaction != null)
                Transaction.Commit();
        }

        public void Dispose()
        {
            if (Conn != null)
                Conn.Dispose();
        }

        protected int ExecuteNonQuery(string cmdText, params DbParameter[] parameters)
        {
            DbCommand cmd = Conn.CreateCommand();

            if (!_autoCommit)
                cmd.Transaction = Transaction;
            cmd.CommandText = cmdText;
            if (parameters != null && parameters.Length > 0)
                cmd.Parameters.AddRange(parameters);

            return cmd.ExecuteNonQuery();
        }

        protected DbDataReader ExecuteReader(string cmdText, params DbParameter[] parameters)
        {
            DbCommand cmd = Conn.CreateCommand();

            if (!_autoCommit)
                cmd.Transaction = Transaction;

            cmd.CommandText = cmdText;
            if (parameters != null && parameters.Length > 0)
                cmd.Parameters.AddRange(parameters);

            return cmd.ExecuteReader();
        }

        public virtual int Insert(string table, string[] columns,
            object[] values)
        {
            if (string.IsNullOrEmpty(table))
                throw new ArgumentNullException("table");

            if (columns == null ||
                columns.Length == 0)
                throw new ArgumentException("columns");

            if (values == null || values.Length == 0)
                throw new ArgumentException("values");

            string sql = "insert into {0}({1}) values({2})";

            int length = values.Length;
            string[] holders = new string[length];
            DbParameter[] parameters = new DbParameter[length];
            for (int i = 0; i < holders.Length; i++)
            {
                holders[i] = "@" + i;
                parameters[i] = NewParameter();
                parameters[i].ParameterName = holders[i];
                parameters[i].Value = values[i];
            }

            string cmdText = string.Format(sql, table,
                string.Join(",", columns),
                string.Join(",", holders));

            return ExecuteNonQuery(cmdText, parameters);
        }

        public virtual Dictionary<string, object> Select(
            string table,
            string[] columns,
            Exp expression)
        {
            return new Dictionary<string, object>();
        }

        protected abstract DbParameter NewParameter();
    }
}
